﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "StructureSlopeLine.h"
#include "AuxiliaryElementUtils.h"
#include "DbObjectUtils.h"
#include "ElementId.h"
#include "GbmpBuiltInCategoryUniIdentitiesNew.h"
#include "ICategory.h"
#include "IElementBasicInformation.h"
#include "IElementModelShape.h"
#include "IElementOffsetComponent.h"
#include "IElementParameterBindings.h"
#include "IElementParameters.h"
#include "IElementParentReporter.h"
#include "IElementPosition.h"
#include "IElementShapeHandle.h"
#include "IElementViewSpecificShapeComponent.h"
#include "IGenericElement.h"
#include "IGraphicsElementShape.h"
#include "IGraphicsLine.h"
#include "IGraphicsPlane.h"
#include "IGraphicsStyleManager.h"
#include "IGraphicsText.h"
#include "IJoinRelationshipBehavior.h"
#include "ILine2d.h"
#include "ILine3d.h"
#include "IModelView.h"
#include "IParameter.h"
#include "IParameterBinding.h"
#include "IParameterDefinitionLibrary.h"
#include "IParameterValueElementId.h"

#include "IPositionAssociatedCoordinate.h"
#include "IPositionCurve2d.h"
#include "StructureSlopeLineBehaviors.h"
#include "Vector3dUtils.h"
#include "IGeometryRelationshipComponent.h"
#include "ImplementationUidTreeRegister.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;



DBOBJECT_DATA_DEFINE(StructureSlopeLine)
{
    m_pOwnerElement = nullptr;

    SetSlopeDefinitionType__(StructureSlopeDefinitionType::ElevationDifference);

    SetStartElevationOffset__(0.0);
    SetEndElevationOffset__(0.0);

}


gcmp::NdbObject* gcmp::StructureSlopeLine::GetTopOwnerObject() const
{
    return quick_cast<NdbObject>(const_cast<IElement*>(GetOwnerElement()));
}

gcmp::UniIdentity gcmp::StructureSlopeLine::GetImplementationUid()
{
    static UniIdentity s_implementationUid = UniIdentity::Create(
        GuidUtils::FromString(L"{75980BE4-DE59-4324-9290-9D5AACC81C83}"), L"StructureSlopeLine");
    return s_implementationUid;
}

IDocument * gcmp::StructureSlopeLine::GetDocument() const
{
    if (!m_pOwnerElement)
    {
        return nullptr;
    }
    return m_pOwnerElement->GetDocument();
}

ElementId gcmp::StructureSlopeLine::GetOwnerElementId() const
{
    if (!m_pOwnerElement)
    {
        return ElementId::InvalidID;
    }
    return m_pOwnerElement->GetElementId();
}

bool StructureSlopeLine::SetOwnerElement(IElement* pOwnerElement)
{
    m_pOwnerElement = pOwnerElement;
    return true;
}

void gcmp::StructureSlopeLine::UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds)
{
    IElement* pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"IElement为空",L"GDMPLab",L"2024-03-30");

    //参数绑定，并且绑定的不是族参数时
    IElementParameterBindings* pBehavior = GetOwnerElement()->GetElementParameters()->GetElementParameterBindings();
    if (pBehavior)
    {
        auto& bindingInfos = pBehavior->GetParameterBindings();
        for (int ii = (int)bindingInfos.size(); ii > 0; --ii)
        {
            const IParameterBinding* info = bindingInfos[ii - 1].get();
            IElement* pSourceElement = m_pOwnerElement ? m_pOwnerElement->GetDocument()->GetElement(info->GetSourceElementId()) : nullptr;
            if (pSourceElement == nullptr)
            {
                continue;
            }

            IElementParameterBindings* pBingBehavior = pSourceElement->GetElementParameters()->GetElementParameterBindings();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pBingBehavior, L"pBingBehavior无效",L"GDMPLab",L"2024-03-30");

            std::vector<RegenDataId> rcDataIds = pBingBehavior->GetParameterRdIdsByParameterDefinitionId(info->GetSourceParameterDefinitionId());
            std::size_t size = rcDataIds.size();
            if (size > 0 && size != 2) // FamFamilyManager为2
            {
                if (deletedElementIds.find(info->GetSourceElementId()) != deletedElementIds.end())
                {
                    pBehavior->DeleteParameterBinding(info->GetTargetParameterDefinitionId());
                }
            }
        }
    }
}

void gcmp::StructureSlopeLine::ReportParents(IElementParentReporter& reporter) const
{
    const IElement* pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"IElement为空",L"GDMPLab",L"2024-03-30");
    const IElementPosition *posBehavior = pElement->GetElementPosition();
    if (posBehavior != nullptr)
    {
        reporter.ReportStrong(posBehavior->GetBaseAssociatedPlaneId());
    }

    const IGeometryRelationshipComponent* pGeometryRepCpt = pElement->GetGeometryRelationshipComponent();
    if (pGeometryRepCpt) {
        const IJoinRelationshipBehavior*pJoineHelper = pGeometryRepCpt->GetJoinRelationship();
        if (pJoineHelper != nullptr)
        {
            reporter.ReportWeak(pJoineHelper->GetElementJoinIdsAtStart());
            reporter.ReportWeak(pJoineHelper->GetElementJoinIdsAtEnd());
            reporter.ReportWeak(pJoineHelper->GetElementJoinIdsAtMiddle());
        }
    }

    const IElementParameterBindings* pBehavior = GetOwnerElement()->GetElementParameters()->GetElementParameterBindings();
    if (pBehavior)
    {
        auto& bindingInfos = pBehavior->GetParameterBindings();
        FOR_EACH(info, bindingInfos)
        {
            IElement* pSourceElement = m_pOwnerElement ? m_pOwnerElement->GetDocument()->GetElement(info->GetSourceElementId()) : nullptr;
            if (pSourceElement != nullptr)
            {
                reporter.ReportWeak(pSourceElement->GetElementId());
            }
        }
    }

    auto& embeddedParamMap = GetOwnerElement()->GetElementParameters()->GetEmbeddedParametersMap();
    FOR_EACH(iter, embeddedParamMap)
    {
        const IParameterValueElementId *pElementIdParamValue = dynamic_cast<const IParameterValueElementId *>(iter.second->GetParameterValueStorage());
        if (pElementIdParamValue != nullptr)
        {
            reporter.ReportWeak(pElementIdParamValue->GetValue());
        }
    }

    reporter.ReportStrong(GetOwnerElement()->GetBasicInformation()->GetTypeId());

    const IGraphicsElementShape* pGrep = pElement->GetElementModelShape()->GetGraphicsElementShape();
    if (pGrep)
    {
        std::vector<ElementId> ids;
        pGrep->GetReferencedElementIds(ids);
        reporter.ReportWeak(ids);
    }

    reporter.ReportStrong(this->GetInstanceId());
}

bool gcmp::StructureSlopeLine::FinishEditing()
{
    std::vector<ElementId> ids = AuxiliaryElementUtils::GetAuxiliaryElements(GetOwnerElement());
    for (int i = 0; i < (int)ids.size(); i++)
    {
        IElement* pElement = m_pOwnerElement ? m_pOwnerElement->GetDocument()->GetElement(ids[i]) : nullptr;
        IElementShapeHandle* pElementShapeHandle = quick_cast<IElementShapeHandle>(pElement);
        if (pElementShapeHandle)
        {
            pElementShapeHandle->FinishEditing();
            continue;
        }
    }
    return true;
}


StructureSlopeLine* StructureSlopeLine::Create(
    IDocument* pDoc,
    const Vector3d& startPt,
    const Vector3d& endPt,
    ElementCreationOptions eco /*= ElementCreationOptions::Normal */)
{
     DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"invalid params",L"GDMPLab",L"2024-03-30");
    OwnerPtr<StructureSlopeLine> opSlopeLine = NEW_AS_OWNER_PTR(StructureSlopeLine);
    StructureSlopeLine* pSlopeLine = opSlopeLine.get();

    IGenericElement* pGenericElement = IGenericElement::Create(
        pDoc, 
        TransferOwnership(opSlopeLine), 
        StructureSlopeLine::GetClassId().GetGuid(), 
        eco
    );

    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGenericElement, L"pGenericElement为空",L"GDMPLab",L"2024-03-30");
    UniIdentity categoryUid = BuiltInCategoryUniIdentities::BICU_STRUCTURE_SLOPELINE;
    pGenericElement->GetBasicInformation()->SetCategoryUid(categoryUid);
    pGenericElement->GetBasicInformation()->SetDefaultTypeName(GBMP_TR(L"坡度线"));
    pGenericElement->GetBasicInformation()->SetImplementationUid(StructureSlopeLine::GetImplementationUid());

    pGenericElement->SetElementCopyPasteComponent(NEW_AS_OWNER_PTR(StructureSlopeLineCopyPasteComponent));
    if (pGenericElement->GetElementParameters())
        pGenericElement->GetElementParameters()->SetElementParametersCustomizer(NEW_AS_OWNER_PTR(StructureSlopeLineParametersCustomizer));
    pGenericElement->SetElementCopyStrategyComponent(NEW_AS_OWNER_PTR(StructureSlopeLineCopyStrategyComponent));
    pGenericElement->SetElementTransformationComponent(NEW_AS_OWNER_PTR(StructureSlopeLineTransformationComponent));
    pGenericElement->SetViewSpecificShapeComponent(NEW_AS_OWNER_PTR(StructureSlopeLineViewSpecificShapeComponent));

    std::vector<Vector3d> points = {startPt, endPt};
    IElement* pElement = pSlopeLine->GetOwnerElement();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElement, L"IElement为空",L"GDMPLab",L"2024-03-30");

    Coordinate3d worldCoor = Coordinate3d::CreateFromAxis(Vector3d(0, 0, 0), Vector3d::UnitX, Vector3d::UnitY);
    IElementPosition::CreateCurve2dOnAssociatedCoordinatePositionBehavior(pElement, worldCoor, points);
    IElementPosition* pSlopeLineElementPosition = pGenericElement->GetElementPosition();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pSlopeLineElementPosition, L"pSlopeLineElementPosition为空",L"GDMPLab",L"2024-03-30");

    IPositionAssociatedCoordinate* pAssociatedCoordinate = quick_cast<IPositionAssociatedCoordinate>(pSlopeLineElementPosition->GetPositionAssociatedPlane());
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pAssociatedCoordinate, L"pAssociatedCoordinate为空",L"GDMPLab",L"2024-03-30");

    pAssociatedCoordinate->SetOffset(startPt.GetZ());
    pElement->SetPositionPoints(NEW_AS_OWNER_PTR(StructureSlopeLinePositionPoints, pElement));
    pSlopeLine->SetSlopeDefinitionType(StructureSlopeDefinitionType::ElevationDifference);
    pSlopeLine->SetStartElevationOffset__(0.0);
    pSlopeLine->SetEndElevationOffset__(0.0);

    return quick_cast<StructureSlopeLine>(pGenericElement->GetExternalObject());
}

Vector3d gcmp::StructureSlopeLine::GetStart() const
{
    const IElement* pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_UNLESS(pElement, Vector3d::Zero, L"IElement为空",L"GDMPLab",L"2024-03-30");
    const IElementPosition* pPosBehavior = pElement->GetElementPosition();
    DBG_WARN_AND_RETURN_UNLESS(pPosBehavior, Vector3d::Zero, L"pPosBehavior为空。",L"GDMPLab",L"2024-03-30");

    return pPosBehavior->GetPointByIndex(0);
}

Vector3d gcmp::StructureSlopeLine::GetEnd() const
{
    const IElement* pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_UNLESS(pElement, Vector3d::Zero, L"IElement为空",L"GDMPLab",L"2024-03-30");
    const IElementPosition* pPosBehavior = pElement->GetElementPosition();
    DBG_WARN_AND_RETURN_UNLESS(pPosBehavior, Vector3d::Zero, L"pPosBehavior为空。",L"GDMPLab",L"2024-03-30");

    return pPosBehavior->GetPointByIndex(1);
}

double gcmp::StructureSlopeLine::GetSlopeAngle() const
{
    Vector3d dirStartEndHorizontal = Vector3d(this->GetEnd().Vec2d(), 0.0) - Vector3d(this->GetStart().Vec2d(), 0.0);
    dirStartEndHorizontal.Normalize();
    Vector3d dirStartEndReal = Vector3d(this->GetEnd().Vec2d(), this->GetEndElevationOffset()) - Vector3d(this->GetStart().Vec2d(), this->GetStartElevationOffset());
    dirStartEndReal.Normalize();

    double slopeAngle = Vector3dUtils::GetAcuteAngle(dirStartEndHorizontal, dirStartEndReal);
    if (MathUtils::IsGreaterThan(this->GetStartElevationOffset(), this->GetEndElevationOffset(), Constants::LENGTH_EPS))
    {
        slopeAngle *= -1;
    }

    return slopeAngle;
}

OwnerPtr<ILine3d> gcmp::StructureSlopeLine::GetSlopeLine() const
{
    Vector3d start = Vector3d(this->GetStart().Vec2d(), 0) + Vector3d::UnitZ * this->GetStartElevationOffset();
    Vector3d end = Vector3d(this->GetEnd().Vec2d(), 0) + Vector3d::UnitZ * this->GetEndElevationOffset();
    return ILine3d::Create(start, end);
}

OwnerPtr<IGraphicsLine> StructureSlopeLine::GetGraphicsLine() const
{
    OwnerPtr<IGraphicsLine> opLine = nullptr;
    const IElement* pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElement, L"IElement为空",L"GDMPLab",L"2024-03-30");
    const IElementPosition* pPosBehavior = pElement->GetElementPosition();
    if (pPosBehavior)
    {
        const IPositionCurve2d* pPosCurve = dynamic_cast<const IPositionCurve2d*>(pPosBehavior->GetPositionGeometry());
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPosCurve, L"pPosCurve为空",L"GDMPLab",L"2024-03-30");
        const IElementPosition* pPosAssociatedCoorBehavior = dynamic_cast<const IElementPosition*>(pPosBehavior);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPosAssociatedCoorBehavior, L"pPosAssociatedCoorBehavior为空",L"GDMPLab",L"2024-03-30");
        const IPositionAssociatedCoordinate* pPosAssociatedCoor = dynamic_cast<const IPositionAssociatedCoordinate*>(pPosAssociatedCoorBehavior->GetPositionAssociatedPlane());
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPosAssociatedCoor, L"pPosAssociatedCoor为空",L"GDMPLab",L"2024-03-30");
        const ILine2d* pBaseLine = dynamic_cast<const ILine2d*>(pPosCurve->GetBaseCurve());
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pBaseLine, L"pPosAssociatedCoor为空",L"GDMPLab",L"2024-03-30");
        opLine = IGraphicsLine::Create(
            Vector3d(pBaseLine->GetStartPoint(), pPosAssociatedCoor->GetOffset()), 
            Vector3d(pBaseLine->GetEndPoint(), pPosAssociatedCoor->GetOffset())
        );

        Matrix4d mat;
        pPosBehavior->GetBasePlaneCoordinate3d().GetWorldMatrix(mat);
        opLine->Transform(mat);
    }

    return opLine;
}

OwnerPtr<IGraphicsElementShape> gcmp::StructureSlopeLine::GetGraphicsLineAndArrow() const
{
    OwnerPtr<IGraphicsElementShape> opGRep = IGraphicsElementShape::Create(GraphicsRenderLayer::ProfileCurve);
    opGRep->SetIsClippable(false);

    ElementId ownerId = GetOwnerElement() ? GetOwnerElement()->GetElementId() : ElementId::InvalidID;
    opGRep->SetElementId(ownerId);
    opGRep->SetGraphicsStyleId(GetDocument()->GetGraphicsStyleManager()->GetGraphicsStyleIdByCategoryUid(this->GetOwnerElement()->GetBasicInformation()->GetCategoryUid()));

    // 绘制直线
    OwnerPtr<IGraphicsLine> opLine = this->GetGraphicsLine();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opLine, L"opLine为空",L"GDMPLab",L"2024-03-30");
    opGRep->AddChild(TransferOwnership(opLine.get()->Clone()));

    // 绘制箭头
    {
        Vector3d startPt = opLine->GetStartPoint();
        Vector3d endPt = opLine->GetEndPoint();
        Vector3d lineDir = startPt - endPt;
        lineDir.Normalize();
        Vector3d arrowDir1 = Vector3dUtils::RotateVectorAroundAxis(lineDir, Vector3d::UnitZ, gcmp::Constants::MATH_PI / 4);
        Vector3d arrowDir2 = Vector3dUtils::RotateVectorAroundAxis(lineDir, Vector3d::UnitZ, -gcmp::Constants::MATH_PI / 4);
        double arrowLength = 800 * std::sqrt(2.0);
        Vector3d arrow1EndPt = endPt + arrowDir1 * arrowLength;
        Vector3d arrow2EndPt = endPt + arrowDir2 * arrowLength;

        OwnerPtr<IGraphicsLine> opArrow1 = IGraphicsLine::Create(endPt, arrow1EndPt);
        OwnerPtr<IGraphicsLine> opArrow2 = IGraphicsLine::Create(endPt, arrow2EndPt);
        opGRep->AddChild(TransferOwnership(opArrow1));
        opGRep->AddChild(TransferOwnership(opArrow2));
    }

    return opGRep;
}


REGISTER_IMPLEMENTATION_UID_TREE_RELATION(IGenericElement, StructureSlopeLine);

